1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.reflect;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static com.google.common.base.Preconditions.checkState;
22
23 import com.google.common.annotations.Beta;
24 import com.google.common.base.Joiner;
25 import com.google.common.base.Objects;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.Maps;
28
29 import java.lang.reflect.GenericArrayType;
30 import java.lang.reflect.ParameterizedType;
31 import java.lang.reflect.Type;
32 import java.lang.reflect.TypeVariable;
33 import java.lang.reflect.WildcardType;
34 import java.util.Arrays;
35 import java.util.Map;
36 import java.util.concurrent.atomic.AtomicInteger;
37
38 import javax.annotation.Nullable;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 @Beta
55 public final class TypeResolver {
56
57 private final TypeTable typeTable;
58
59 public TypeResolver() {
60 this.typeTable = new TypeTable();
61 }
62
63 private TypeResolver(TypeTable typeTable) {
64 this.typeTable = typeTable;
65 }
66
67 static TypeResolver accordingTo(Type type) {
68 return new TypeResolver().where(TypeMappingIntrospector.getTypeMappings(type));
69 }
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 public TypeResolver where(Type formal, Type actual) {
91 Map<TypeVariableKey, Type> mappings = Maps.newHashMap();
92 populateTypeMappings(mappings, checkNotNull(formal), checkNotNull(actual));
93 return where(mappings);
94 }
95
96
97 TypeResolver where(Map<TypeVariableKey, ? extends Type> mappings) {
98 return new TypeResolver(typeTable.where(mappings));
99 }
100
101 private static void populateTypeMappings(
102 final Map<TypeVariableKey, Type> mappings, Type from, final Type to) {
103 if (from.equals(to)) {
104 return;
105 }
106 new TypeVisitor() {
107 @Override void visitTypeVariable(TypeVariable<?> typeVariable) {
108 mappings.put(new TypeVariableKey(typeVariable), to);
109 }
110 @Override void visitWildcardType(WildcardType fromWildcardType) {
111 WildcardType toWildcardType = expectArgument(WildcardType.class, to);
112 Type[] fromUpperBounds = fromWildcardType.getUpperBounds();
113 Type[] toUpperBounds = toWildcardType.getUpperBounds();
114 Type[] fromLowerBounds = fromWildcardType.getLowerBounds();
115 Type[] toLowerBounds = toWildcardType.getLowerBounds();
116 checkArgument(
117 fromUpperBounds.length == toUpperBounds.length
118 && fromLowerBounds.length == toLowerBounds.length,
119 "Incompatible type: %s vs. %s", fromWildcardType, to);
120 for (int i = 0; i < fromUpperBounds.length; i++) {
121 populateTypeMappings(mappings, fromUpperBounds[i], toUpperBounds[i]);
122 }
123 for (int i = 0; i < fromLowerBounds.length; i++) {
124 populateTypeMappings(mappings, fromLowerBounds[i], toLowerBounds[i]);
125 }
126 }
127 @Override void visitParameterizedType(ParameterizedType fromParameterizedType) {
128 ParameterizedType toParameterizedType = expectArgument(ParameterizedType.class, to);
129 checkArgument(fromParameterizedType.getRawType().equals(toParameterizedType.getRawType()),
130 "Inconsistent raw type: %s vs. %s", fromParameterizedType, to);
131 Type[] fromArgs = fromParameterizedType.getActualTypeArguments();
132 Type[] toArgs = toParameterizedType.getActualTypeArguments();
133 checkArgument(fromArgs.length == toArgs.length,
134 "%s not compatible with %s", fromParameterizedType, toParameterizedType);
135 for (int i = 0; i < fromArgs.length; i++) {
136 populateTypeMappings(mappings, fromArgs[i], toArgs[i]);
137 }
138 }
139 @Override void visitGenericArrayType(GenericArrayType fromArrayType) {
140 Type componentType = Types.getComponentType(to);
141 checkArgument(componentType != null, "%s is not an array type.", to);
142 populateTypeMappings(mappings, fromArrayType.getGenericComponentType(), componentType);
143 }
144 @Override void visitClass(Class<?> fromClass) {
145
146
147
148 throw new IllegalArgumentException("No type mapping from " + fromClass);
149 }
150 }.visit(from);
151 }
152
153
154
155
156
157 public Type resolveType(Type type) {
158 checkNotNull(type);
159 if (type instanceof TypeVariable) {
160 return typeTable.resolve((TypeVariable<?>) type);
161 } else if (type instanceof ParameterizedType) {
162 return resolveParameterizedType((ParameterizedType) type);
163 } else if (type instanceof GenericArrayType) {
164 return resolveGenericArrayType((GenericArrayType) type);
165 } else if (type instanceof WildcardType) {
166 return resolveWildcardType((WildcardType) type);
167 } else {
168
169 return type;
170 }
171 }
172
173 private Type[] resolveTypes(Type[] types) {
174 Type[] result = new Type[types.length];
175 for (int i = 0; i < types.length; i++) {
176 result[i] = resolveType(types[i]);
177 }
178 return result;
179 }
180
181 private WildcardType resolveWildcardType(WildcardType type) {
182 Type[] lowerBounds = type.getLowerBounds();
183 Type[] upperBounds = type.getUpperBounds();
184 return new Types.WildcardTypeImpl(
185 resolveTypes(lowerBounds), resolveTypes(upperBounds));
186 }
187
188 private Type resolveGenericArrayType(GenericArrayType type) {
189 Type componentType = type.getGenericComponentType();
190 Type resolvedComponentType = resolveType(componentType);
191 return Types.newArrayType(resolvedComponentType);
192 }
193
194 private ParameterizedType resolveParameterizedType(ParameterizedType type) {
195 Type owner = type.getOwnerType();
196 Type resolvedOwner = (owner == null) ? null : resolveType(owner);
197 Type resolvedRawType = resolveType(type.getRawType());
198
199 Type[] args = type.getActualTypeArguments();
200 Type[] resolvedArgs = resolveTypes(args);
201 return Types.newParameterizedTypeWithOwner(
202 resolvedOwner, (Class<?>) resolvedRawType, resolvedArgs);
203 }
204
205 private static <T> T expectArgument(Class<T> type, Object arg) {
206 try {
207 return type.cast(arg);
208 } catch (ClassCastException e) {
209 throw new IllegalArgumentException(arg + " is not a " + type.getSimpleName());
210 }
211 }
212
213
214 private static class TypeTable {
215 private final ImmutableMap<TypeVariableKey, Type> map;
216
217 TypeTable() {
218 this.map = ImmutableMap.of();
219 }
220
221 private TypeTable(ImmutableMap<TypeVariableKey, Type> map) {
222 this.map = map;
223 }
224
225
226 final TypeTable where(Map<TypeVariableKey, ? extends Type> mappings) {
227 ImmutableMap.Builder<TypeVariableKey, Type> builder = ImmutableMap.builder();
228 builder.putAll(map);
229 for (Map.Entry<TypeVariableKey, ? extends Type> mapping : mappings.entrySet()) {
230 TypeVariableKey variable = mapping.getKey();
231 Type type = mapping.getValue();
232 checkArgument(!variable.equalsType(type), "Type variable %s bound to itself", variable);
233 builder.put(variable, type);
234 }
235 return new TypeTable(builder.build());
236 }
237
238 final Type resolve(final TypeVariable<?> var) {
239 final TypeTable unguarded = this;
240 TypeTable guarded = new TypeTable() {
241 @Override public Type resolveInternal(
242 TypeVariable<?> intermediateVar, TypeTable forDependent) {
243 if (intermediateVar.getGenericDeclaration().equals(var.getGenericDeclaration())) {
244 return intermediateVar;
245 }
246 return unguarded.resolveInternal(intermediateVar, forDependent);
247 }
248 };
249 return resolveInternal(var, guarded);
250 }
251
252
253
254
255
256
257
258
259
260 Type resolveInternal(TypeVariable<?> var, TypeTable forDependants) {
261 Type type = map.get(new TypeVariableKey(var));
262 if (type == null) {
263 Type[] bounds = var.getBounds();
264 if (bounds.length == 0) {
265 return var;
266 }
267 Type[] resolvedBounds = new TypeResolver(forDependants).resolveTypes(bounds);
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296 if (Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY
297 && Arrays.equals(bounds, resolvedBounds)) {
298 return var;
299 }
300 return Types.newArtificialTypeVariable(
301 var.getGenericDeclaration(), var.getName(), resolvedBounds);
302 }
303
304 return new TypeResolver(forDependants).resolveType(type);
305 }
306 }
307
308 private static final class TypeMappingIntrospector extends TypeVisitor {
309
310 private static final WildcardCapturer wildcardCapturer = new WildcardCapturer();
311
312 private final Map<TypeVariableKey, Type> mappings = Maps.newHashMap();
313
314
315
316
317
318 static ImmutableMap<TypeVariableKey, Type> getTypeMappings(
319 Type contextType) {
320 TypeMappingIntrospector introspector = new TypeMappingIntrospector();
321 introspector.visit(wildcardCapturer.capture(contextType));
322 return ImmutableMap.copyOf(introspector.mappings);
323 }
324
325 @Override void visitClass(Class<?> clazz) {
326 visit(clazz.getGenericSuperclass());
327 visit(clazz.getGenericInterfaces());
328 }
329
330 @Override void visitParameterizedType(ParameterizedType parameterizedType) {
331 Class<?> rawClass = (Class<?>) parameterizedType.getRawType();
332 TypeVariable<?>[] vars = rawClass.getTypeParameters();
333 Type[] typeArgs = parameterizedType.getActualTypeArguments();
334 checkState(vars.length == typeArgs.length);
335 for (int i = 0; i < vars.length; i++) {
336 map(new TypeVariableKey(vars[i]), typeArgs[i]);
337 }
338 visit(rawClass);
339 visit(parameterizedType.getOwnerType());
340 }
341
342 @Override void visitTypeVariable(TypeVariable<?> t) {
343 visit(t.getBounds());
344 }
345
346 @Override void visitWildcardType(WildcardType t) {
347 visit(t.getUpperBounds());
348 }
349
350 private void map(final TypeVariableKey var, final Type arg) {
351 if (mappings.containsKey(var)) {
352
353
354
355
356
357 return;
358 }
359
360 for (Type t = arg; t != null; t = mappings.get(TypeVariableKey.forLookup(t))) {
361 if (var.equalsType(t)) {
362
363
364
365
366 for (Type x = arg; x != null; x = mappings.remove(TypeVariableKey.forLookup(x))) {}
367 return;
368 }
369 }
370 mappings.put(var, arg);
371 }
372 }
373
374
375
376
377
378
379
380
381 private static final class WildcardCapturer {
382
383 private final AtomicInteger id = new AtomicInteger();
384
385 Type capture(Type type) {
386 checkNotNull(type);
387 if (type instanceof Class) {
388 return type;
389 }
390 if (type instanceof TypeVariable) {
391 return type;
392 }
393 if (type instanceof GenericArrayType) {
394 GenericArrayType arrayType = (GenericArrayType) type;
395 return Types.newArrayType(capture(arrayType.getGenericComponentType()));
396 }
397 if (type instanceof ParameterizedType) {
398 ParameterizedType parameterizedType = (ParameterizedType) type;
399 return Types.newParameterizedTypeWithOwner(
400 captureNullable(parameterizedType.getOwnerType()),
401 (Class<?>) parameterizedType.getRawType(),
402 capture(parameterizedType.getActualTypeArguments()));
403 }
404 if (type instanceof WildcardType) {
405 WildcardType wildcardType = (WildcardType) type;
406 Type[] lowerBounds = wildcardType.getLowerBounds();
407 if (lowerBounds.length == 0) {
408 Type[] upperBounds = wildcardType.getUpperBounds();
409 String name = "capture#" + id.incrementAndGet() + "-of ? extends "
410 + Joiner.on('&').join(upperBounds);
411 return Types.newArtificialTypeVariable(
412 WildcardCapturer.class, name, wildcardType.getUpperBounds());
413 } else {
414
415 return type;
416 }
417 }
418 throw new AssertionError("must have been one of the known types");
419 }
420
421 private Type captureNullable(@Nullable Type type) {
422 if (type == null) {
423 return null;
424 }
425 return capture(type);
426 }
427
428 private Type[] capture(Type[] types) {
429 Type[] result = new Type[types.length];
430 for (int i = 0; i < types.length; i++) {
431 result[i] = capture(types[i]);
432 }
433 return result;
434 }
435 }
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451 static final class TypeVariableKey {
452 private final TypeVariable<?> var;
453
454 TypeVariableKey(TypeVariable<?> var) {
455 this.var = checkNotNull(var);
456 }
457
458 @Override public int hashCode() {
459 return Objects.hashCode(var.getGenericDeclaration(), var.getName());
460 }
461
462 @Override public boolean equals(Object obj) {
463 if (obj instanceof TypeVariableKey) {
464 TypeVariableKey that = (TypeVariableKey) obj;
465 return equalsTypeVariable(that.var);
466 } else {
467 return false;
468 }
469 }
470
471 @Override public String toString() {
472 return var.toString();
473 }
474
475
476 static Object forLookup(Type t) {
477 if (t instanceof TypeVariable) {
478 return new TypeVariableKey((TypeVariable<?>) t);
479 } else {
480 return null;
481 }
482 }
483
484
485
486
487
488 boolean equalsType(Type type) {
489 if (type instanceof TypeVariable) {
490 return equalsTypeVariable((TypeVariable<?>) type);
491 } else {
492 return false;
493 }
494 }
495
496 private boolean equalsTypeVariable(TypeVariable<?> that) {
497 return var.getGenericDeclaration().equals(that.getGenericDeclaration())
498 && var.getName().equals(that.getName());
499 }
500 }
501 }